package database;

import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Calendar;
import event.Event;

public abstract class EventSerializer {
	private static EventSerializer sInstance = null;
	private static boolean usePostgre = false;
	private static boolean useMySQL = false;
	protected static String DB_DRIVER = null;
	protected static String DB_CONNECTION = null;
	protected static String DB_USER = null;
	protected static String DB_PASSWORD = null;

	// use custom database credentials
	private final static void setCredentials(boolean usePostgre,
			boolean useMySQL, String dbURL, String dbUser, String dbPassword) {
		EventSerializer.usePostgre = usePostgre;
		EventSerializer.useMySQL = useMySQL;

		EventSerializer.DB_CONNECTION = dbURL;
		EventSerializer.DB_USER = dbUser;
		EventSerializer.DB_PASSWORD = dbPassword;
	}

	// use default database credentials
	public final static void setCredentials(boolean usePostgre, boolean useMySQL) {
		EventSerializer.usePostgre = usePostgre;
		EventSerializer.useMySQL = useMySQL;
	}

	public final static EventSerializer instance() {
		if (sInstance == null) {
			try {
				if (usePostgre)
					sInstance = new EventSerializerPostgreSQL();
				else if (useMySQL)
					sInstance = new EventSerializerMySQL();
				else
					return null;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		} else
			try {
				if (!sInstance.isConnected())
					sInstance.connect();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}

		return sInstance;
	}

	public final static EventSerializer instance(boolean usePostgre,
			boolean useMySQL, String dbURL, String dbUser, String dbPassword) {
		setCredentials(usePostgre, useMySQL, dbURL, dbUser, dbPassword);
		if (sInstance != null) {
			sInstance.finalize();
			sInstance = null;
		}

		if (sInstance == null) {
			try {
				if (usePostgre)
					sInstance = new EventSerializerPostgreSQL();
				else if (useMySQL)
					sInstance = new EventSerializerMySQL();
				else
					return null;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		} else
			try {
				if (!sInstance.isConnected())
					sInstance.connect();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}

		return sInstance;
	}

	protected java.sql.Connection connection = null;
	protected PreparedStatement prepStatement = null;
	protected int teller = 0;
	// number of inserts before actual commit
	protected int insertBatchSize = 85;

	public final void setInsertBatchSize(int size) {
		insertBatchSize = size;
	}

	protected void finalize() {
		try {
			if (isConnected()) {
				commitInsertBatch();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public final boolean isConnected() throws SQLException {
		return connection != null && !connection.isClosed();
	}

	public final void connect() throws ClassNotFoundException, SQLException {
		if (connection != null)
			connection.close();

		Class.forName(DB_DRIVER);
		connection = null;
		connection = DriverManager.getConnection(DB_CONNECTION, DB_USER,
				DB_PASSWORD);
		connection.setAutoCommit(false);
	}

	public abstract void storeEvent(Event e);

	public void commitInsertBatch() {
		if (prepStatement == null || teller == 0)
			return;

		if (teller == insertBatchSize) {
			try {
				prepStatement.execute();
				connection.commit();
				prepStatement.close();

				teller = 0;
				prepStatement = null;
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		} else {
			try {
				// fill the preparedStatement with dummy values before
				// committing
				Calendar calendar = Calendar.getInstance();
				java.util.Date now = calendar.getTime();
				java.sql.Timestamp currentTimestamp = new java.sql.Timestamp(
						now.getTime());
				byte[] dummy = new byte[0];
				while (teller < insertBatchSize) {
					prepStatement.setLong(teller * 7 + 1, -1);
					prepStatement.setLong(teller * 7 + 2, -1);
					prepStatement
							.setTimestamp(teller * 7 + 3, currentTimestamp);
					prepStatement.setBoolean(teller * 7 + 4, true);
					prepStatement.setBoolean(teller * 7 + 5, true);
					prepStatement.setBoolean(teller * 7 + 6, true);
					prepStatement.setBytes(teller * 7 + 7, dummy);

					teller++;
				}

				prepStatement.execute();
				connection.commit();
				prepStatement.close();

				teller = 0;
				prepStatement = null;
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
		}
	}

	public abstract Event retrieveEvent(long ID);

	public abstract long getMaximumSerializedID();

	public abstract void truncateSerializedEvents();

	public abstract void createTable();

	public abstract void dropTable();
}
